#ifndef AVALON_SERVICELOCATORSERVER_HPP
#define AVALON_SERVICELOCATORSERVER_HPP
#include "Avalon/IO/ConnectionState.hpp"
#include "Avalon/Serialization/Serialization.hpp"
#include "Avalon/ServiceLocator/Directory.hpp"
#include "Avalon/ServiceLocator/ServiceLocatorServices.hpp"
#include "Avalon/Services/ServerMixin.hpp"
#include "Avalon/Services/ServiceNode.hpp"
#include "Avalon/SignalHandling/ConnectionGroup.hpp"
#include "Avalon/SignalHandling/TaskSignalHandler.hpp"
#include "Avalon/Threading/Async.hpp"

namespace Avalon {
namespace ServiceLocator {

  /*! \class ServiceLocatorServer
   *  \brief Server side of the service locator.
   *  \author Kamal Mansouri <kranar@hotmail.com>
   */
  class ServiceLocatorServer :
      private Services::ServerMixin<ServiceLocatorChannel> {
    public:

      //! Constructs a ServiceLocatorServer.
      /*!
        \param dataStore The data store to connect to.
        \param connection The ServerConnection accepting incoming connections.
        \param dataShuttleFactory Specifies the type of serialization to use.
        \param timeout The Connection timeout.
      */
      ServiceLocatorServer(ServiceLocatorDataStore* dataStore,
        IO::ServerConnection* connection,
        Serialization::DataShuttleFactory* dataShuttleFactory,
        boost::posix_time::time_duration timeout =
        Services::ServiceNode::TIMEOUT);

      virtual ~ServiceLocatorServer();

      //! Returns <code>true</code> iff the ServiceLocatorServer is running.
      bool IsRunning();

      //! Starts the ServiceLocatorServer.
      /*!
        \throw IOException if the server failed to start.
      */
      void Start();

      //! Stops the ServiceLocatorServer.
      /*!
        \throw IOException if the server failed to shutdown properly.
      */
      void Stop();

    private:
      struct ServiceEntryListing;
      struct SubscriberEntry;
      boost::mutex m_mutex;
      boost::scoped_ptr<ServiceLocatorDataStore> m_dataStore;
      IO::ConnectionState m_dataStoreState;
      boost::scoped_ptr<IO::ServerConnection> m_connection;
      IO::ConnectionState m_connectionState;
      bool m_isRunning;
      bool m_isShuttingDown;
      std::vector<ServiceLocatorChannel*> m_clients;
      std::map<std::string, ServiceLocatorChannel*> m_sessions;
      Services::ServiceNode m_service;
      std::map<std::string, ServiceEntryListing> m_serviceListings;
      std::map<int, ServiceEntry> m_serviceEntries;
      std::map<unsigned int, SubscriberEntry> m_subscriptions;
      int m_nextServiceId;
      boost::condition_variable m_closedCondition;
      SignalHandling::TaskSignalHandler m_signalHandler;

      // Helper methods.
      void Shutdown();
      virtual void RemoveClient(ServiceLocatorChannel* channel);
      void DeleteEntry(const DirectoryEntry::Tag& tag,
        const DirectoryEntry::Tag& parent);
      void PublishDirectoryEntryMoved(const DirectoryEntry::Tag& tag,
        const DirectoryEntry::Tag& parent, bool added);
      bool HasPermission(unsigned int account, unsigned int directory,
        Directory::Permission permission);
      void OnConnectionAccepted(
        const Threading::Async<IO::Channel*>::Ptr& result);
      void OnDataStoreClosed();
      void OnConnectionClosed();
      void OnClientClosed(ServiceLocatorChannel* client);

      // Requests.
      void OnLoginRequest(const Services::RequestToken<LoginService>& request,
        const std::string& username, const std::string& password);
      void OnRegisterRequest(
        const Services::RequestToken<RegisterService>& request,
        const std::string& name, const std::string& details);
      void OnUnregisterRequest(
        const Services::RequestToken<UnregisterService>& request,
        int serviceId);
      void OnLocateRequest(const Services::RequestToken<LocateService>& request,
        const std::string& name);
      void OnSubscribeRequest(
        const Services::RequestToken<SubscribeAvailabilityService>& request,
        const std::string& serviceName);
      void OnUnsubscribeRequest(
        const Services::RequestToken<UnsubscribeAvailabilityService>& request,
        const std::string& serviceName);
      void OnLoadDirectoryEntryRequest(
        const Services::RequestToken<LoadDirectoryEntryService>& request,
        unsigned int id);
      void OnLoadParentDirectoryRequest(
        const Services::RequestToken<LoadParentDirectoryService>& request,
        unsigned int id);
      void OnCreateAccountRequest(
        const Services::RequestToken<CreateAccountService>& request,
        const std::string& name, const DirectoryEntry::Tag& directory,
        const std::string& password);
      void OnCreateDirectoryRequest(
        const Services::RequestToken<CreateDirectoryService>& request,
        const std::string& name, const DirectoryEntry::Tag& parent);
      void OnMoveEntryRequest(
        const Services::RequestToken<MoveEntryService>& request,
        unsigned int entryId, unsigned int directoryId);
      void OnDeleteEntryRequest(
        const Services::RequestToken<DeleteEntryService>& request,
        unsigned int entryId);
      void OnSetPermissionsRequest(
        const Services::RequestToken<SetPermissionsService>& request,
        unsigned int accountId, unsigned int entryId,
        Directory::Permission permissions);
      void OnSessionAuthenticationRequest(
        const Services::RequestToken<SessionAuthenticationService>& request,
        const std::string& sessionId, unsigned int saltId);
  };
}
}

#endif // AVALON_SERVICELOCATORSERVER_HPP
